﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

using SmlEngine;
using SmlEngine.Sprites;

namespace SmlEngine.Sprites.Collections
{
    public class QuadTree<T> where T : IGameObject
    {
        private Dictionary<Vector2, List<IGameObject>> cells;
        private List<IGameObject> items;

        public Vector2 CellSize
        {
            get;
            private set;
        }

        public int Count
        {
            get
            {
                return cells.Count;
            }
        }

        public QuadTree(Vector2 cellSize)
        {
            CellSize = cellSize;
            cells = new Dictionary<Vector2, List<IGameObject>>();
            items = new List<IGameObject>();
        }

        public List<IGameObject> this[Vector2 cell]
        {
            get
            {
                if (!cells.ContainsKey(cell))
                {
                    return null;
                }
                return cells[cell];
            }
        }

        public IGameObject this[int index]
        {
            get
            {
                return items[index];
            }
        }

        public bool ContainsCell(Vector2 cellPosition)
        {
            return cells.ContainsKey(cellPosition);
        }

        public void Add(IGameObject item)
        {
            var intersectingCells = GetIntersectingCells(item);
            foreach (var cell in intersectingCells)
            {
                if (!cells.ContainsKey(cell))
                {
                    cells.Add(cell, new List<IGameObject>());
                }
                cells[cell].Add(item);
            }
            items.Add(item);
        }

        public void Remove(IGameObject item)
        {
            Vector2 cellPosition = (item.Position / CellSize).Floor();
            if (!cells.ContainsKey(cellPosition))
            {
                return;
            }
            cells[cellPosition].Remove(item);
            items.Remove(item);
        }

        public void Update(GameTime gameTime)
        {
            foreach (var cell in cells)
            {
                foreach (IGameObject item in cell.Value)
                {
                    if (item.Visibility == Visibility.Active || item.Visibility == Visibility.Visible)
                    {
                        Vector2 oldPosition = item.Position;
                        item.Update(gameTime);
                        if (item.Position != oldPosition)
                        {
                            this.Remove(item);
                            this.Add(item);
                        }
                    }
                }
            }
        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            foreach (var cell in cells)
            {
                cell.Value.ForEach(item => item.Draw(gameTime, spriteBatch));
            }
        }

        public List<Vector2> GetIntersectingCells(IGameObject item)
        {
            Vector2 topleft = (item.Position / CellSize).Floor();
            Vector2 topRight = (new Vector2(item.Position.X + item.Size.X, item.Position.Y) / CellSize).Floor();
            Vector2 bottomLeft = (new Vector2(item.Position.X, item.Position.Y + item.Size.Y) / CellSize).Floor();
            Vector2 bottomRight = (new Vector2(item.Position.X + item.Size.X, item.Position.Y + item.Size.Y) / CellSize).Floor();

            List<Vector2> result = new List<Vector2>();
            result.Add(topleft);
            result.Add(topRight);
            result.Add(bottomLeft);
            result.Add(bottomRight);

            return result.Distinct().ToList();
        }

        public IEnumerator<IGameObject> GetEnumerator()
        {
            return items.GetEnumerator();
        }
    }
}
